﻿using System;
using System.Net.Sockets;
using Framework.Socket;
using MangoCannon.Framework.Util;
using System.Threading;
using System.Threading.Tasks;
using System.Text;
using System.Collections;
using System.Net;
using System.IO;

namespace MangoCannon.Framework.Socket
{
    public class SocketService : ISocketService
    {
        private static ArrayList listener;
        private static bool isSleep;//全局睡眠
        private static bool isStart = false;
        private Thread listenThread;

        public SocketService()
        {
            listener = new ArrayList();
        }

        
        #region 事件委托
        public event EventHandler listenStarted;

        public event EventHandler listenStop;

        public event EventHandler statusChange;

        public event EventHandler clientJoin;

        public event EventHandler clientLeave;

        public event EventHandler clientKicked;

        public event EventHandler clientRealJoin;

        public event EventHandler clientMessage;
        #endregion


        private void initListener(string alias,int port)
        {
                if (checkIsExists(alias))
                {
                    Console.WriteLine("Can not insert ["+alias+"] instance , Becuse one server instance is exists.");
                    Logger.getLogger().writeLog(this, "Can not insert [" + alias + "] instance , Becuse one server instance is exists.");
                    return;
                }
                try
                {
                    ServerInstance serverInstance = new ServerInstance { alias = alias, listener = new TcpListener(IPAddress.Any, port) };
                    serverInstance.listener.Start();
                    int idx = listener.Add(serverInstance);
                    isSleep = false;
                    
                    listenStarted(this, new ServiceEventArgs { serverAlias = alias , clientAlias=""});
                }
                catch (Exception ex)
                {
                    Logger.getLogger().writeLog(this, "监听错误", ex);
                }

        }
        private ServerInstance getServer(string alias)
        {
            object[] instances = listener.ToArray();
            foreach (object _item in listener)
            {
                ServerInstance item = (ServerInstance)_item;
                if (item.alias == alias)
                {
                    return item;
                }
            }
            return null;
        }
        private bool checkIsExists(string alias)
        {
            if (getServer(alias)!=null)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        public bool awaken(string serverAlias)
        {
            if (listener != null && isSleep)
            {
                isSleep = false;
            }
            return isSleep;
        }

        public bool setServerConfig(string serverAlias, ServerConfig config)
        {
            if (!isStart)
            {
                if (checkIsExists(serverAlias))
                {
                    TcpListener tl = getServer(serverAlias).listener;
                    tl.Server.DualMode = config.DualMode;
                    tl.Server.DontFragment = config.DontFragment;
                    tl.Server.LingerState = config.LingerState;
                    tl.Server.ReceiveTimeout = config.ReceiveTimeout;
                    tl.Server.SendBufferSize = config.SendBufferSize;
                    tl.Server.SendTimeout = config.SendTimeout;
                    tl.Server.Ttl = config.Ttl;
                }
                return true;
            }
            else
            {
                throw new Exception("Server is running,no config!");
            }
        }

        public bool sleep(string serverAlias)
        {
            if (listener!=null && !isSleep)
            {
                isSleep = true;
            }
            return isSleep;
        }

        public bool start(string serverAlias, int port, int buffSize = 1024, int overTime = 3000)
        {
            initListener(serverAlias,port);
            pong();
            return true;
        }

        private async void pong()
        {
            if (!isStart)
            {
                isStart = true;
                int fll = 0;
                while (isStart)
                {
                    if (!isSleep)
                    {
                        //逐个服务器实例接收客户
                        var ist = ((ServerInstance)listener[fll]);
                        try
                        {
                            var client = await ist.listener.AcceptTcpClientAsync();
                            if (!ist.isSleep)
                            {
                                await HandleClientAsync(client, ist);
                            }
                        }
                        catch (Exception)
                        {
                            continue;
                        }
                        
                        //异步处理每个请求，while不会被阻塞
                        fll++;
                        if (fll > listener.Count - 1)
                        {
                            fll = 0;
                        }
                    }
                    else
                    {
                        Thread.Sleep(1000);
                        continue;
                    }
                }
            }
        }
        private Task HandleClientAsync(TcpClient tcpClient,ServerInstance ist)
        {
            clientJoin(this, new ServiceEventArgs { clientAlias = tcpClient.Client.RemoteEndPoint.ToString()});
            return Task.Run(new Action(()=> {
                //每个客户一个新线程
                //客户端别名就是远端IP与端口
                bool firstChecked = false;
                NetworkStream clientStream = tcpClient.GetStream();
                SocketError errorCode = SocketError.Success;
                byte[] message = new byte[8192];
                string firstHex = "";
                int bytesRead;
                ASCIIEncoding ecd = new ASCIIEncoding();
                UTF8Encoding ucd = new UTF8Encoding();
                while (isStart)
                {
                    if (!ist.isSleep)
                    {
                        bytesRead = 0;
                        try
                        {
                            if (!firstChecked)
                            {
                                //blocks until a client sends a message
                                bytesRead = clientStream.Read(message, 0, 64);
                                if (bytesRead >= 7 && bytesRead <= 64)
                                {
                                    //Console.WriteLine("fnc:"+ bytesRead);
                                    try
                                    {
                                        firstHex = ecd.GetString(message, 0, bytesRead);//以接收回来的普通文本初始化firstHex
                                    }
                                    catch (Exception)
                                    {
                                    }
                                }
                            }
                            else
                            {
                                //blocks unftil a client sends a message
                                bytesRead = clientStream.Read(message, 0, 8192);
                            }
                        }
                        catch (SocketException se)
                        {
                            if (se.SocketErrorCode == SocketError.TimedOut)
                            {
                                errorCode = SocketError.TimedOut;
                            }
                            //a socket error has occured
                            break;
                        }
                        catch (IOException)
                        {
                            errorCode = SocketError.ConnectionReset;
                            break;//IO流中断
                        }
                        catch (Exception ex)
                        {
                            Logger.getLogger().writeLog(this, ex.GetType().ToString(), ex);
                        }

                        if (bytesRead == 0)
                        {
                            //the client has disconnected from the server
                            errorCode = SocketError.HostDown;
                            break;
                        }

                        if (!firstChecked && null != message && !"".Equals(firstHex))
                        {
                            firstHex = HexUtil.bytesToHexString(message);
                            string sysFirstHex = HexUtil.bytesToHexString(HexUtil.FIRST_BEAT);
                            if (firstHex.Contains(sysFirstHex))
                            {
                                firstChecked = true;
                                string alias = tcpClient.Client.RemoteEndPoint.ToString();
                                //客户句柄处理
                                IPEndPoint ep = (IPEndPoint)tcpClient.Client.RemoteEndPoint;

                                ClientManager.getIns().setHandle(alias, new ClientHandle { alias = alias, loginTime = DateTime.Now.ToString(), ipAddress = ep.Address });
                                clientRealJoin(this, new ServiceEventArgs { clientAlias = tcpClient.Client.RemoteEndPoint.ToString(), serverAlias = ist.alias });
                            }
                        }
                        else if (firstChecked && null != message)
                        {
                            clientMessage(this, new ServiceMessageArgs { clientAlias = tcpClient.Client.RemoteEndPoint.ToString(), message = ucd.GetString(message, 0, bytesRead) });
                        }
                    }
                    else
                    {
                        Thread.Sleep(500);
                        continue;
                    }
                }
                clientLeave(this, new ServiceEventArgs { clientAlias = tcpClient.Client.RemoteEndPoint.ToString(), serverAlias = ist.alias, error = errorCode });
                if (null != ClientManager.getIns().getHandle(tcpClient.Client.RemoteEndPoint.ToString()))
                {
                    ClientManager.getIns().rmHandle(tcpClient.Client.RemoteEndPoint.ToString());
                }
                tcpClient.Dispose();
            }));
        }

        public bool stop(string serverAlias)
        {
            ServerInstance si = getServer(serverAlias);
            if (null != si)
            {
                si.listener.Stop();
                return true;
            }
            else
            {
                return false;
            }
        }

        public void shutdown()
        {
            isStart = false;
            Thread.Sleep(200);
            object[] instances = listener.ToArray();
            foreach (object _item in listener)
            {
                try
                {
                    ServerInstance item = (ServerInstance)_item;
                    item.listener.Stop();
                }
                catch (Exception)
                {
                    Logger.getLogger().writeLog(this, "Shut the server instance error!");
                }
            }
            isStart = false;
        }
    }

    public class ServerInstance
    {
        public string alias { get; set; }
        public TcpListener listener { get; set; }
        public bool isSleep { get; set; }
    }
    public class ServiceEventArgs : EventArgs
    {
        public string serverAlias { get; set; }
        public string clientAlias { get; set; }
        /// <summary>
        /// 只记录TimeOut|HostDown|ConnectionReset
        /// </summary>
        public SocketError error { get; set; }
    }
    public class ServiceMessageArgs : EventArgs
    {
        public string serverAlias { get; set; }
        public string clientAlias { get; set; }
        public string message { get; set; }
    }
}
